﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Utilities;

namespace DataTraining
{
    public class WordDistance
    {
        Dictionary<string, Dictionary<string, int>> lLeftWord;
        Dictionary<string, Dictionary<string, int>> lRightWord;
        Dictionary<string, Dictionary<string, double>> pLeftWord;
        Dictionary<string, Dictionary<string, double>> pRightWord;
        Dictionary<string, double> lDisL;
        Dictionary<string, double> lDisR;
        HashSet<string> lWord;
        List<string> lWordTemp;

        public WordDistance()
        {
            lLeftWord = new Dictionary<string, Dictionary<string, int>>();
            lRightWord = new Dictionary<string, Dictionary<string, int>>();
            pLeftWord = new Dictionary<string, Dictionary<string, double>>();
            pRightWord = new Dictionary<string, Dictionary<string, double>>();
            lDisL = new Dictionary<string, double>();
            lDisR = new Dictionary<string, double>();
            lWord = new HashSet<string>();
            lWordTemp = new List<string>();
        }

        private string removeTag(string input)
        {
            return Regex.Replace(input, @"\[[^\]]+\]", "");
        }
        private int sumValue(Dictionary<string, int> i)
        {
            int temp = 0;
            foreach (var item in i)
            {
                temp += item.Value;
            }
            return temp;
        }

        private void getTempWord(string path)
        {
            Dictionary<string, int> lTemp = new Dictionary<string, int>();
            string[] files = Directory.GetFiles(path);
            for (int i = 0; i < files.Length; i++)
            {
                StreamReader sr = new StreamReader(files[i]);
                string line = "";
                while ((line = sr.ReadLine()) != null)
                {
                    string[] words = line.Split(' ');
                    foreach (var item in words)
                    {
                        if (lTemp.ContainsKey(item))
                            lTemp[item]++;
                        else
                            lTemp.Add(item, 1);
                    }
                }
                sr.Close();
                Debugger.drawProgressBar(files[i], i, files.Length, 50, '-');
            }
            var query = from k in lTemp
                        where k.Value > 5
                        select k.Key;
            lWordTemp = query.ToList();
        }
        private void genLeftRight(string path)
        {
            StreamReader sr = new StreamReader(path);
            string line = "";
            while ((line = sr.ReadLine()) != null)
            {
                string[] words = line.Split(' ');
                for (int i = 0; i < words.Length; i++)
                {
                    if (words.Length > 1)
                        if (i == 0)
                        {
                            string w1 = words[i];
                            string w2 = words[i + 1];
                            if (lWordTemp.Contains(w1))
                                if (lRightWord.ContainsKey(w1))
                                {
                                    if (lRightWord[w1].ContainsKey(w2))
                                        lRightWord[w1][w2]++;
                                    else
                                        lRightWord[w1][w2] = 1;
                                }
                                else
                                {
                                    lRightWord[w1] = new Dictionary<string, int>();
                                    lRightWord[w1].Add(w2, 1);
                                }

                        }
                        else if (i == words.Length - 1)
                        {
                            string w1 = words[i];
                            string w2 = words[i - 1];
                            if (lWordTemp.Contains(w1))
                                if (lLeftWord.ContainsKey(w1))
                                {
                                    if (lLeftWord[w1].ContainsKey(w2))
                                        lLeftWord[w1][w2]++;
                                    else
                                        lLeftWord[w1][w2] = 1;
                                }
                                else
                                {
                                    lLeftWord[w1] = new Dictionary<string, int>();
                                    lLeftWord[w1].Add(w2, 1);
                                }
                        }
                        else
                        {
                            string w1 = words[i - 1];
                            string w2 = words[i];
                            string w3 = words[i + 1];
                            if (lWordTemp.Contains(w2))
                            {
                                if (lRightWord.ContainsKey(w2))
                                {
                                    if (lRightWord[w2].ContainsKey(w3))
                                        lRightWord[w2][w3]++;
                                    else
                                        lRightWord[w2][w3] = 1;
                                }
                                else
                                {
                                    lRightWord[w2] = new Dictionary<string, int>();
                                    lRightWord[w2].Add(w1, 1);
                                }
                                if (lLeftWord.ContainsKey(w2))
                                {
                                    if (lLeftWord[w2].ContainsKey(w1))
                                        lLeftWord[w2][w1]++;
                                    else
                                        lLeftWord[w2][w1] = 1;
                                }
                                else
                                {
                                    lLeftWord[w2] = new Dictionary<string, int>();
                                    lLeftWord[w2].Add(w3, 1);
                                }
                            }
                        }
                }
            }
            sr.Close();
        }
        private void genProbabilityLeftRight()
        {
            int sum = 0;
            foreach (var item in lLeftWord)
            {
                pLeftWord.Add(item.Key, new Dictionary<string, double>());
                sum = sumValue(item.Value);
                foreach (var iitem in item.Value)
                {
                    pLeftWord[item.Key].Add(iitem.Key, iitem.Value * 1.0 / sum);
                }
            }
            foreach (var item in lRightWord)
            {
                pRightWord.Add(item.Key, new Dictionary<string, double>());
                sum = sumValue(item.Value);
                foreach (var iitem in item.Value)
                {
                    pRightWord[item.Key].Add(iitem.Key, iitem.Value * 1.0 / sum);
                }
            }
        }
        private void genDistanceLeftRight()
        {
            string[] items;
            foreach (var item in lWord)
            {
                if (!lDisL.ContainsKey(item))
                {
                    items = item.Split(' ');
                    lDisL.Add(item, DisL(items[0], items[1]));
                }
                if (!lDisR.ContainsKey(item))
                {
                    items = item.Split(' ');
                    lDisR.Add(item, DisR(items[0], items[1]));
                }
            }
        }
        private void writeLRWord(string path)
        {
            StreamWriter sw = new StreamWriter(path + "/lleftword.txt");
            string line = "";
            var items = from pair in lLeftWord
                        orderby pair.Key ascending
                        select pair;
            foreach (var i in items)
            {
                var iitems = from pair in i.Value
                             orderby pair.Key ascending
                             select pair;
                line = i.Key + "\n";
                foreach (var ii in iitems)
                {
                    line += ii.Key + ":" + ii.Value + "\t";
                }
                sw.WriteLine(line);
            }
            sw.Close();
            sw = new StreamWriter(path + "/rrightword.txt");
            items = from pair in lRightWord
                    orderby pair.Key ascending
                    select pair;
            foreach (var i in items)
            {
                var iitems = from pair in i.Value
                             orderby pair.Key ascending
                             select pair;
                line = i.Key + "\n";
                foreach (var ii in iitems)
                {
                    line += ii.Key + ":" + ii.Value + "\t";
                }
                sw.WriteLine(line);
            }
            sw.Close();
        }
        private void writeLRPro(string path)
        {
            StreamWriter sw = new StreamWriter(path + "/leftpro.txt");
            string line = "";
            var items = from pair in pLeftWord
                        orderby pair.Key ascending
                        select pair;
            foreach (var i in items)
            {
                var iitems = from pair in i.Value
                             orderby pair.Key ascending
                             select pair;
                line = i.Key + "\n";
                foreach (var ii in iitems)
                {
                    line += ii.Key + ":" + ii.Value + "\t";
                }
                sw.WriteLine(line);
            }
            sw.Close();
            sw = new StreamWriter(path + "/rightpro.txt");
            items = from pair in pRightWord
                    orderby pair.Key ascending
                    select pair;
            foreach (var i in items)
            {
                var iitems = from pair in i.Value
                             orderby pair.Key ascending
                             select pair;
                line = i.Key + "\n";
                foreach (var ii in iitems)
                {
                    line += ii.Key + ":" + ii.Value + "\t";
                }
                sw.WriteLine(line);
            }
            sw.Close();
        }
        private void writeLRDis(string path)
        {
            StreamWriter sw = new StreamWriter(path + "/leftdis.txt");
            string line = "";
            var items = from pair in lDisL
                        orderby pair.Value descending
                        select pair;
            foreach (var i in items)
            {
                line = i.Key + "\t" + i.Value;
                sw.WriteLine(line);
            }
            sw.Close();
            sw = new StreamWriter(path + "/rightdis.txt");
            items = from pair in lDisR
                    orderby pair.Value descending
                    select pair;
            foreach (var i in items)
            {
                line = i.Key + "\t" + i.Value;
                sw.WriteLine(line);
            }
            sw.Close();
        }
        public double DisL(string w1, string w2)
        {
            double score = 0;
            if (pLeftWord.ContainsKey(w1) && pLeftWord.ContainsKey(w2))
            {
                Dictionary<string, double> lw1 = pLeftWord[w1];
                Dictionary<string, double> lw2 = pLeftWord[w2];
                foreach (var item in lw1)
                {
                    if (lw2.ContainsKey(item.Key))
                    {
                        score += Math.Min(lw1[item.Key], lw2[item.Key]);
                    }
                }
            }
            return score;
        }
        public double DisR(string w1, string w2)
        {
            double score = 0;
            if (pRightWord.ContainsKey(w1) && pRightWord.ContainsKey(w2))
            {
                Dictionary<string, double> lw1 = pRightWord[w1];
                Dictionary<string, double> lw2 = pRightWord[w2];
                foreach (var item in lw1)
                {
                    if (lw2.ContainsKey(item.Key))
                    {
                        score += Math.Min(lw1[item.Key], lw2[item.Key]);
                    }
                }
            }
            return score;
        }

        public void process(string pathIn, string pathOut)
        {
            string[] files = Directory.GetFiles(pathIn);
            Debugger.log("File count: " + files.Length);
            Debugger.log("Get Temp Word");
            getTempWord(pathIn);
            Debugger.log("Temp Word: " + lWordTemp.Count);

            Debugger.log("Gen Left right...");
            for (int i = 0; i < files.Length; i++)
            {
                genLeftRight(files[i]);
                Debugger.drawProgressBar(files[i], i, files.Length, 50, '-');
            }
            Debugger.log("Word count: " + lWord.Count);
            Debugger.log("Write file");
            writeLRWord(pathOut);
            Debugger.log("Gen Pro Left right...");
            genProbabilityLeftRight();
            Debugger.log("Write file");
            writeLRPro(pathOut);
            //Debugger.log("Gen Dis Left right...");
            //genDistanceLeftRight();
            //Debugger.log("Write file");
            //writeLRDis(pathOut);
            Debugger.log("OK");
        }
    }
}
